package models

import (
	"go/ast"
	"strings"
)

type PackageInfo struct {
	Name    string
	Path    string
	Imports Imports
}

// Package is the package info
type Package struct {
	PackageInfo
	InterfaceInfos map[string]*InterfaceInfo
	StructInfos    map[string]*StructInfo
}

func (p *Package) Visit(node ast.Node) (w ast.Visitor) {
	switch n := node.(type) {
	case *ast.File:
		for _, imp := range n.Imports {
			i := &Import{}
			p.Imports = append(p.Imports, i)
			ast.Walk(i, imp)
		}
	case *ast.GenDecl:
		genType := NewGenType(p)
		return genType
	case *ast.FuncDecl:
		m := &MethodInfo{}
		ast.Walk(m, n)
		structName := strings.TrimPrefix(m.RecvType, "*")
		p.StructInfos[structName].Methods = append(p.StructInfos[structName].Methods, m)
		return nil
	}
	return p
}

func NewPackage(name, path string) *Package {
	return &Package{
		PackageInfo: PackageInfo{
			Name:    name,
			Path:    path,
			Imports: make([]*Import, 0),
		},
		InterfaceInfos: make(map[string]*InterfaceInfo),
		StructInfos:    make(map[string]*StructInfo),
	}
}

func getType(expr ast.Expr) string {
	switch t := expr.(type) {
	case *ast.Ident:
		return t.Name
	case *ast.StarExpr:
		return "*" + getType(t.X)
	case *ast.SelectorExpr:
		return getType(t.X) + "." + getType(t.Sel)
	case *ast.ArrayType:
		return "[]" + getType(t.Elt)
	case *ast.MapType:
		return "map[" + getType(t.Key) + "]" + getType(t.Value)
	case *ast.InterfaceType:
		return "interface{}"
	case *ast.FuncType:
		return "func(...)"
	case *ast.ChanType:
		return "chan " + getType(t.Value)
	case *ast.Ellipsis:
		return "..." + getType(t.Elt)
	}
	return ""
}
